//
//  GLTextureLoader.swift
//  learn-gl-es-swift
//
//  Created by LimitLiu on 2019/9/9.
//  Copyright © 2019 LimitLiu. All rights reserved.
//

import UIKit
import GLKit

struct GLTextureInfo {
    var name: GLuint
    var target: GLenum
    var width: GLsizei
    var height: GLsizei
}

enum GLPowerOf2: GLsizei {
    case gl1 = 1
    case gl2 = 2
    case gl4 = 4
    case gl8 = 8
    case gl16 = 16
    case gl32 = 32
    case gl64 = 64
    case gl128 = 128
    case gl256 = 256
    case gl512 = 512
    case gl1024 = 1024
}

class GLTextureLoader: NSObject {
    class func data(
        resizedCGImage image: CGImage,
        width: inout GLsizei,
        height: inout GLsizei
        ) -> UnsafeMutablePointer<UInt8>
    {
        let oriW = GLsizei(image.width)
        let oriH = GLsizei(image.height)
        assert(oriW > 0, "Invalid image width.")
        assert(oriH > 0, "Invalid image height.")
        width = calculate(dimension: oriW)
        height = calculate(dimension: oriH)
        let imageData = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(width * height * 4))
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        if let context = CGContext(
            data: imageData,
            width: Int(width),
            height: Int(height),
            bitsPerComponent: 8,
            bytesPerRow: 4 * Int(width),
            space: colorSpace,
            bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
            ) {
            context.translateBy(x: 0, y: CGFloat(height))
            context.scaleBy(x: 1, y: -1)
            context.draw(image, in: CGRect(x: 0, y: 0, width: CGFloat(width), height: CGFloat(height)))
            return imageData
        } else {
            fatalError("Can't create CGContext")
        }
    }
    class func texture(
        image: CGImage,
        options: [String: NSNumber]? = nil
        ) throws -> GLTextureInfo {
        var w: GLsizei = GLsizei()
        var h: GLsizei = GLsizei()
        let imageData: UnsafeMutablePointer<UInt8> =
            data(resizedCGImage: image, width: &w, height: &h)
        var textureBufferId: GLuint = GLuint()
        glGenTextures(1, &textureBufferId)
        glBindTexture(GLenum(GL_TEXTURE_2D), textureBufferId)
        glTexImage2D(GLenum(GL_TEXTURE_2D), 0, GL_RGBA, w, h, 0, GLenum(GL_RGBA), GLenum(GL_UNSIGNED_BYTE), imageData)
        glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MIN_FILTER), GL_LINEAR)
        return GLTextureInfo(name: textureBufferId, target: GLenum(GL_TEXTURE_2D), width: w, height: h)
    }
    
    class func calculate(dimension: GLsizei) -> GLsizei {
        var result = GLPowerOf2.gl1.rawValue
        
        if dimension > GLPowerOf2.gl512.rawValue {
            result = GLPowerOf2.gl1024.rawValue
        } else if dimension > GLPowerOf2.gl256.rawValue {
            result = GLPowerOf2.gl512.rawValue
        } else if dimension > GLPowerOf2.gl128.rawValue {
            result = GLPowerOf2.gl256.rawValue
        } else if dimension > GLPowerOf2.gl64.rawValue {
            result = GLPowerOf2.gl128.rawValue
        } else if dimension > GLPowerOf2.gl32.rawValue {
            result = GLPowerOf2.gl64.rawValue
        } else if dimension > GLPowerOf2.gl16.rawValue {
            result = GLPowerOf2.gl32.rawValue
        } else if dimension > GLPowerOf2.gl8.rawValue {
            result = GLPowerOf2.gl16.rawValue
        } else if dimension > GLPowerOf2.gl4.rawValue {
            result = GLPowerOf2.gl8.rawValue
        } else if dimension > GLPowerOf2.gl2.rawValue {
            result = GLPowerOf2.gl4.rawValue
        } else if dimension > GLPowerOf2.gl1.rawValue {
            result = GLPowerOf2.gl2.rawValue
        }
        return result
    }
}
